纷享为什么 wait(),notify(),notifyAll() 的调用必须在 synchronized 代码中 2019-07-02
关键字:Java,多线程
Java中,任何对象都拥有一个Monitor的概念,这个Monitor拥有单独的一个锁,并拥有一个入队列和一个等待队列。没有被synchronized修饰的方法或语句,可以在任何时间被任何线程调用。而被synchronized修饰的方法同一时间仅能被一个拥有该对象锁的线程调用。当一个线程在调用synchronized方法时,另外的线程调用该方法时就被阻塞,并放入队列中。
多线程开发中,wait(),notify(),notifyAll()方法必须在synchronized方法或代码块中,否则会抛出运行时异常:java.lang.IllegalMonitorStateException: current thread not owner。因为要想调用这些方法,必须要保证调用方法的线程已经获取了该对象的锁。
示例代码
|
|
|
|
wait() 方法会释放所持有的锁
当一个线程在调用wait()方法时,该线程会释放当前所持有的锁,并进入等待队列。因此要想执行该方法,前提是必须拥有这个锁,而synchronized可以保证在执行wait()时,已经拿到了锁。
什么情况下我们才会使用wait()
在等待队列中的线程只会被notify()、notifyAll()唤醒并执行。
一般情况下,我们在调用wait()时,肯定会加上一个限定条件。也就是说在达到某个条件之后,我们才会让当前线程wait()。而notify()、notifyAll()也是在达到某个条件之后,我们才会去唤醒其他的等待线程。而这个条件就是线程间进行通信的一个互斥条件。
生产者消费者模式解读
以生产者消费者模式为例,消费者从缓存中取数据,生产者向缓存中写数据。消费者必须等生产者写入数据后才能取数据,生产者必须等消费者把数据取走之后才能向缓存中写数据。如果wait(),notify(),notifyAll()可以被任意方法调用,当消费者在读数据的时候,发现缓存为空,这时调用了wait()方法,而此时生产者也调用了notify(),告诉消费者有数据到达,这样消费者就永远接收不到这个通知,然后一直等下去。
因此,我们必须在synchronized方法中调用wait(),notify(),notifyAll(),从而互斥地拥有锁,从而保证不会丢失通知。
参考文章:Why wait(), notify(), notifyAll() must be called inside a synchronized method/block?